package server;
import common.*;
import java.net.MalformedURLException;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;

/**
 * Classe usata per andare a chiamare la logica del server in seguito ad una richiesta ricevuta da un client connesso tramite protocollo RMI. La classe si carica sulla macchina fisica su cui gira, permettendo di essere scaricata la sua interfaccia dai client connessi tramite protocollo RMI. 
 */
public class Server_RMI extends UnicastRemoteObject implements ServerInterface_RMI, Runnable{
	
	private static final long serialVersionUID = 1l;
	private Gioco gioco;
	private Partita partita;
	@SuppressWarnings("unused")
	private Registry registro;
	private ArrayList<ClientInterface_RMI> listafrmi = new ArrayList<ClientInterface_RMI>();
	private ArrayList<Integer> listatoken = new ArrayList<Integer>();
	private static final String port = "1099";
	
	/**
	 * Costruttore di classe che imposta i riferimenti alle classi necessarie per gestire la partita.
	 * @param gioco Oggetto Gioco.
	 * @param partita Oggetto Partita.
	 * @throws RemoteException Eccezione lanciata in caso di errore di connessione con i client.
	 */
	public Server_RMI(Gioco gioco, Partita partita) throws RemoteException{
		
		this.gioco = gioco;
		this.partita = partita;
	}
	
	/**
	 * Metodo che permette di far partire il thread dedicato alla classe. Il server si carica sulla macchina fisica sulla porta di default del protocollo RMI.
	 */
	public void run(){
		try{
			registro = LocateRegistry.createRegistry(Integer.parseInt(port));
			Naming.bind("rmi://127.0.0.1/server:" + port, (Remote) this);
			Naming.rebind("rmi://127.0.0.1/server:" + port, (Remote) this);
			System.out.println("Server Avviato");
		} catch(AccessException e){
			e.printStackTrace();
		} catch(RemoteException e){
			e.printStackTrace();
		} catch(MalformedURLException e){
			e.printStackTrace();
		} catch (AlreadyBoundException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public synchronized void registrazione(String ip, int tok){
		
		try {
			listafrmi.add( (ClientInterface_RMI) Naming.lookup("rmi://" + ip + "/client:" + port));
			listatoken.add(tok);
			System.out.println("Client RMI preso !!!");
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public synchronized ArrayList<Object> creaUtente(String usr, String pass) {
		System.out.println("Ricevuta richiesta creaUtente");
		return gioco.creaUtente(usr, pass);
	}

	@Override
	public synchronized ArrayList<Object> login(String usr, String pass){
		System.out.println("Ricevuta richiesta login");
		return gioco.login(usr, pass);
	}
	
	@Override
	public synchronized ArrayList<Object> creaRazza(int tok,String nome,String ti){
		System.out.println("Ricevuta richiesta creaRazza");
		return gioco.creaRazza(tok, nome,ti);
	}
	
	@Override
	public synchronized ArrayList<Object> accessoPartita(int tok){
		System.out.println("Ricevuta richiesta accessoPartita");
		
		return partita.accessoPartita(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> uscitaPartita(int tok){
		System.out.println("Ricevuta richiesta uscitaPartita");
		return partita.uscitaPartita(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> listaGiocatori(int tok){
		System.out.println("Ricevuta richiesta listaGiocatori");
		return partita.listaGiocatori(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> classifica(int tok){
		System.out.println("Ricevuta richiesta classifica");
		return gioco.classifica(tok);
	}
	
	/**
	 * Metodo che permette la disconnessione del client. Se disconnesso, l'interfaccia memorizzata viene eliminata dall'array in cui sono memorizzate tutte le interfacce scaricate.
	 */
	public synchronized ArrayList<Object> logout(int tok){
		System.out.println("Ricevuta richiesta logout");
		int i;
		
		i = listatoken.indexOf(tok);
		listafrmi.remove(i);
		listatoken.remove(i);
		System.out.println("Client RMI eliminato");
		return gioco.logout(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> mappaGenerale(int tok){
		System.out.println("Ricevuta richiesta mappaGenerale");
		return partita.mappaGenerale(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> listaDinosauri(int tok){
		System.out.println("Ricevuta richiesta listaDinosauri");
		return partita.listaDinosauri(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> vistaLocale(int tok, String id){
		System.out.println("Ricevuta richiesta vistaLocale");
		return partita.vistaLocale(tok, id);
	}
	
	@Override
	public synchronized ArrayList<Object> statoDinosauro(int tok, String id){
		System.out.println("Ricevuta richiesta statoDinosauro");
		return partita.statoDinosauro(tok, id);
	}
	
	@Override
	public synchronized ArrayList<Object> muoviDinosauro(int tok, String id, int x, int y){
		System.out.println("Ricevuta richiesta muoviDinosauro");
		return partita.muoviDinosauro(tok, id, x, y);
	}
	
	@Override
	public synchronized ArrayList<Object> cresciDinosauro(int tok, String id){
		System.out.println("Ricevuta richiesta cresciDinosauro");
		return partita.cresciDinosauro(tok, id);
	}
	
	@Override
	public synchronized ArrayList<Object> deponiUovo(int tok, String id){
		System.out.println("Ricevuta richiesta deponiUovo");
		return partita.deponiUovo(tok, id);
	}
	
	@Override
	public synchronized ArrayList<Object> confermaTurno(int tok){
		System.out.println("Ricevuta richiesta confermaTurno");
		return partita.confermaTurno(tok);
	}
	
	@Override
	public synchronized ArrayList<Object> passaTurno(int tok){
		System.out.println("Ricevuta richiesta passaTurno");
		return partita.passaTurno(tok);
	}
	
	/**
	 * Questo metodo controlla che il client connesso al server tramite protocollo RMI sia ancora attivo, tramite la chiamata remota di un metodo che restituisce true o false. In base alla risposta del client, il server sa se il client e' attivo o meno: nell'ultimo caso, il client viene eliminato dalla lista delle interfacce attive e chiusa la connessione.<br \>
	 * Una volta controllate le connessioni dei client, invoca i metodi remoti cambioturno su ogni interfaccia dei client disponibili.
	 */
	public void cambioTurno(String username){
		@SuppressWarnings("unused")
		boolean x = false;
		
		System.out.println("SERVER_RMIsize di listafrmi: " + listafrmi.size());
		for( int i = 0; i < listafrmi.size(); i++){
			try {
				x = listafrmi.get(i).exist();
				listafrmi.get(i).cambioTurno(username);
			} catch (RemoteException e) {
				listafrmi.remove(listafrmi.get(i));
				partita.uscitaPartita(listatoken.get(i));
				gioco.logout(listatoken.get(i));
				listatoken.remove(i);
			}
		}
	}
	
	@Override
	public void cambioTurnoPartita(){
		partita.cambioTurno();
	}
	
	/**
	 * Come per il metodo cambioTurno, anche qui, prima di
	 */
	public void specieMorta(String username, Boolean bul){
		for( int i = 0; i < listafrmi.size(); i++){
			try {
				listafrmi.get(i).exist();
				System.out.println("Server_rmi ho ricevuto il ping dal client");
				listafrmi.get(i).specieMorta(username);
			} catch (RemoteException e) {
				listafrmi.remove(i);
				partita.uscitaPartita(listatoken.get(i));
				gioco.logout(listatoken.get(i));
				listatoken.remove(i);
				System.out.println("Client RMI eliminato");
			}
		}
	}
}
